﻿using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace IInject.Tests
{
	/// <summary>
	///This is a test class for ContainerTest and is intended
	///to contain all ContainerTest Unit Tests
	///</summary>
	[TestClass]
	public class ContainerTest
	{
		private static IContainer InitializeContainerAndRegisterDependent()
		{
			var target = new Container();
			return target.Set<Dependent>();
		}

		private static IContainer InitializeContainerAndRegisterIntAndString()
		{
			var target = new Container();
			target.Map<int>().Return(1).Apply();
			return target.Map<string>().Return("2").Apply();
		}

		/// <summary>
		///A test for ResolveAll
		///</summary>
		[TestMethod]
		public void ResolveAllTypeTest()
		{
			var target = new Container();
			var type = typeof (object);
			var expected = new object[] {new DependencyTest(1, "1"), new Dependent()};
			target.SetAll(type, expected);
			var actual = target.GetAll(type);
			Assert.AreEqual(expected.Count(), actual.Count());
		}

		/// <summary>
		///A test for ResolveAll
		///</summary>
		[TestMethod]
		public void MapTFromTToActionTest()
		{
			var target = new Container();
			target.Map<IDependencyTest, DependencyTest>().Apply();
			var type = target.Get<IDependencyTest>();
			Assert.IsTrue(type is DependencyTest);
		}

		/// <summary>
		///A test for ResolveAll
		///</summary>
		[TestMethod]
		public void ResolveAllGenericTypeTest()
		{
			var target = new Container();
			var type = typeof (DependencyTest);
			var expected = new[] {new DependencyTest(1, "1"), new DependencyTest(2, "2")};
			target.SetAll(type, expected);
			var actual = target.GetAll<DependencyTest>();
			Assert.AreEqual(expected.Count(), actual.Count());
		}

		/// <summary>
		///A test for ResolveAll
		///</summary>
		[TestMethod]
		public void ResolveAllGenericWithActionTest()
		{
			var target = new Container();
			target.SetAll<DependencyTest>(b =>
			{
			    b.Add(new DependencyTest(1, "1"));
			    b.Add(new DependencyTest(2, "2"));
			});
			var actual = target.GetAll<DependencyTest>();
			Assert.AreEqual(2, actual.Count());
		}

		/// <summary>
		///A test for ResolveAll
		///</summary>
		[TestMethod]
		public void ResolveAllGenericTFromTToWithActionTest()
		{
			var target = new Container();
			target.SetAll<IDependencyTest, DependencyTest>(b =>
			{
			    b.Add(new DependencyTest(1, "1"));
			    b.Add(new DependencyTest(2, "2"));
			});
			var actual = target.GetAll<IDependencyTest>();
			Assert.AreEqual(2, actual.Count());
		}

        /// <summary>
        ///A test for ResolveAll
        ///</summary>
        [TestMethod]
        public void ResolveAllTypeWithKeyTest()
        {
            const string key = "Test";
            var target = new Container();
            var type = typeof(DependencyTest);
            var expected = new object[] { new DependencyTest(1, "1"), new Dependent() };
            target.SetAll(type, key, expected);
            var actual = target.GetAll(type, key);
            Assert.AreEqual(expected.Count(), actual.Count());
        }

        /// <summary>
        ///A test for ResolveAll
        ///</summary>
        [TestMethod]
        public void ResolveAllGenericTypeWithKeyTest()
        {
            const string key = "Test";
            var target = new Container();
            var type = typeof(DependencyTest);
            var expected = new[] { new DependencyTest(1, "1"), new DependencyTest(2, "2") };
            target.SetAll(type, key, expected);
            var actual = target.GetAll<DependencyTest>(key);
            Assert.AreEqual(expected.Count(), actual.Count());
        }

        /// <summary>
        ///A test for ResolveAll
        ///</summary>
        [TestMethod]
        public void ResolveAllGenericWithKeyActionTest()
        {
            const string key = "Test";
            var target = new Container();
            target.SetAll<DependencyTest>(b =>
            {
                b.With(key);
                b.Add(new DependencyTest(1, "1"));
                b.Add(new DependencyTest(2, "2"));
            });
            var actual = target.GetAll<DependencyTest>(key);
            Assert.AreEqual(2, actual.Count());
        }

        /// <summary>
        ///A test for ResolveAll
        ///</summary>
        [TestMethod]
        public void ResolveAllGenericTFromTToWithKeyActionTest()
        {
            const string key = "Test";
            var target = new Container();
            target.SetAll<IDependencyTest, DependencyTest>(b =>
            {
                b.With(key);
                b.Add(new DependencyTest(1, "1"));
                b.Add(new DependencyTest(2, "2"));
            });
            IEnumerable<IDependencyTest> actual = target.GetAll<IDependencyTest>(key);
            Assert.AreEqual(2, actual.Count());
        }

        /// <summary>
        ///A test for ResolveAll
        ///</summary>
        [TestMethod]
        public void SetAllTypeWithKeyFailsWithDuplicateTypeAndKeyTest()
        {
            const string key = "Test";
            var target = new Container();
            var type = typeof(DependencyTest);
            var expected = new object[] { new DependencyTest(1, "1"), new Dependent() };
            target.SetAll(type, key, expected);

            try
            {
                target.SetAll(type, key, expected);
            }
            catch (RegistrationException)
            {
                return;
            }

            Assert.Fail("SetAllTypeWithKeyFailsWithDuplicateTest did not fail adding duplicate.");
        }

		/// <summary>
		///A test for Resolve
		///</summary>
		public void ResolveGenericWithInstanceTestHelper<T>() where T : class, new()
		{
			var target = new Container();
			var expected = new T();
			target.Set(expected);
			var actual = target.Get<T>();
			Assert.AreEqual(expected, actual);
		}

		[TestMethod]
		public void ResolveGenericWithInstanceTest()
		{
            ResolveGenericWithInstanceTestHelper<Dependent>();
		}

		/// <summary>
		///A test for Resolve
		///</summary>
		[TestMethod]
		public void ResolveWithTypeTest()
		{
			var target = InitializeContainerAndRegisterDependent();
			var type = typeof (DependencyTest);
			target.Set(type);
			var actual = target.Get(type);
			Assert.AreEqual(type, actual.GetType());
			Assert.IsNotNull(((DependencyTest) actual).Child);
		}
        [TestMethod]
        public void ResolveWithKeyTest()
        {
            var target = InitializeContainerAndRegisterDependent();
            const string key = "Test";
            var type = typeof(DependencyTest);
            target.Set(type, key);
            var actual = target.Get(type, key);
            Assert.AreEqual(type, actual.GetType());
            Assert.IsNotNull(((DependencyTest)actual).Child);
        }

		/// <summary>
		///A test for Resolve
		///</summary>
		[TestMethod]
		public void SetTypeWithKeyFailsWithDuplicateTypeAndKeyTest()
        {
            const string key = "Test";
			var target = InitializeContainerAndRegisterDependent();
			var type = typeof(DependencyTest);
			target.Set(type, key);

            try
            {
                target.Set(type, key);
            }
            catch (RegistrationException)
            {
                return;
            }

            Assert.Fail("SetTypeWithKeyFailsWithDuplicateTypeAndKeyTest did not fail with duplicate type and key.");
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericTFromTToTestHelper<TFrom, TTo>() where TTo : class, TFrom
		{
			var target = new Container();
			target.Set<Dependent>();
			var toType = typeof(TTo);
			target.Set<TFrom, TTo>();
			var actual = target.Get<TFrom>();
			Assert.AreEqual(toType, actual.GetType());
		}

		[TestMethod]
		public void RegisterGenericTFromTToTest()
		{
			RegisterGenericTFromTToTestHelper<IDependencyTest, DependencyTest>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericWithKeyAndInstanceAndScopeTestHelper<T>() where T : class, new()
		{
			var target = new Container();
			const string key = "Key";
			var expected = new T();
			target.Set(key, expected, Scope.Singleton);
			var actual = target.Get<T>(key);
			Assert.AreEqual(expected, actual);
		}

		[TestMethod]
		public void RegisterGenericWithKeyAndInstanceAndScopeTest()
		{
            RegisterGenericWithKeyAndInstanceAndScopeTestHelper<Dependent>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericTFromTToWithKeyTestHelper<TFrom, TTo>() where TTo : class, TFrom
		{
			var target = new Container();
			const string key = "Test";
			var expected = typeof(TTo);
			target.Set<TFrom, TTo>(key);
			var actual = target.Get<TFrom>(key);
			Assert.AreEqual(expected, actual.GetType());
		}

		[TestMethod]
		public void RegisterGenericTFromTToWithKeyTest()
		{
            RegisterGenericTFromTToWithKeyTestHelper<AbstractDependent, Dependent2>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericWithInstanceTestHelper<T>() where T : class, new()
		{
			var target = new Container();
			var expected = new T();
			target.Set(expected);
			var actual = target.Get<T>();
			Assert.AreEqual(expected, actual);
		}

		[TestMethod]
		public void RegisterGenericWithInstanceTest()
		{
            RegisterGenericWithInstanceTestHelper<Dependent>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericTFromTToWithKeyAndScopeTestHelper<TFrom, TTo>() where TTo : class, TFrom
		{
			var target = new Container();
			const string key = "Test";
			var expected = typeof(TTo);
			target.Set<TFrom, TTo>(key, Scope.PerResolve);
			var actual = target.Get<TFrom>(key);
			Assert.AreEqual(expected, actual.GetType());
		}

		[TestMethod]
		public void RegisterGenericTFromTToWithKeyAndScopeTest()
		{
            RegisterGenericTFromTToWithKeyAndScopeTestHelper<AbstractDependent, Dependent2>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericTFromTToWithScopeTestHelper<TFrom, TTo>() where TTo : class, TFrom
		{
			var target = new Container();
			var expected = typeof(TTo);
			target.Set<TFrom, TTo>(Scope.PerResolve);
			var actual = target.Get<TFrom>();
			Assert.AreEqual(expected, actual.GetType());
		}

		[TestMethod]
		public void RegisterGenericTFromTToWithScopeTest()
		{
            RegisterGenericTFromTToWithScopeTestHelper<AbstractDependent, Dependent2>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericWithInstanceAndScopeTestHelper<T>() where T : class, new()
		{
			var target = new Container();
			var expected = new T();
			target.Set(expected, Scope.Singleton);
			var actual = target.Get<T>();
			Assert.AreEqual(expected, actual);
		}

		[TestMethod]
		public void RegisterGenericWithInstanceAndScopeTest()
		{
            RegisterGenericWithInstanceAndScopeTestHelper<Dependent>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericWithKeyTestHelper<T>() where T : class
		{
			var target = new Container();
			const string key = "Test";
			var expected = typeof(T);
			target.Set<T>(key);
			var actual = target.Get<T>(key);
			Assert.AreEqual(expected, actual.GetType());
		}

		[TestMethod]
		public void RegisterGenericWithKeyTest()
		{
            RegisterGenericWithKeyTestHelper<Dependent>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeWithKeyTest()
		{
			var target = InitializeContainerAndRegisterIntAndString();
			const string key = "Test";
			var expected = typeof(DependencyTest);
			var actual = target.Set(expected, key).Get(expected, key);
			Assert.AreEqual(expected, actual.GetType());
			Assert.AreEqual(((DependencyTest) actual).Id, 1);
			Assert.AreEqual(((DependencyTest) actual).Name, "2");
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericWithKeyAndInstanceTestHelper<T>() where T : class, new()
		{
			var target = new Container();
			const string key = "Test";
			var expectedInstance = new T();
			target.Set(key, expectedInstance);
			var actual = target.Get<T>(key);
			Assert.AreEqual(expectedInstance, actual);
		}

		[TestMethod]
		public void RegisterGenericWithKeyAndInstanceTest()
		{
            RegisterGenericWithKeyAndInstanceTestHelper<Dependent>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericWithKeyAndScopeTestHelper<T>() where T : class
		{
			var target = new Container();
			const string key = "Test";
			var expected = typeof(T);
			target.Set<T>(key, Scope.PerResolve);
			var actual = target.Get<T>(key);
			Assert.AreEqual(expected, actual.GetType());
		}

		[TestMethod]
		public void RegisterGenericWithKeyAndScopeTest()
		{
            RegisterGenericWithKeyAndScopeTestHelper<Dependent>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		public void RegisterGenericWithScopeTestHelper<T>() where T : class
		{
			var target = new Container();
			var expected = typeof(T);
			target.Set<T>(Scope.Singleton);
			var actual = target.Get<T>();
			Assert.AreEqual(expected, actual.GetType());
		}

		[TestMethod]
		public void RegisterGenericWithScopeTest()
		{
            RegisterGenericWithScopeTestHelper<Dependent>();
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeFromToTest()
		{
			var target = InitializeContainerAndRegisterIntAndString();
			var from = typeof(IDependencyTest);
			var expected = typeof(DependencyTest);
			target.Set(from, expected);
			var actual = target.Get(from);
			Assert.AreEqual(expected, actual.GetType());
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeWithKeyAndInstanceAndScopeTest()
		{
			var target = InitializeContainerAndRegisterIntAndString();
			var type = typeof(DependencyTest);
			const string key = "Test";
			var expected = new DependencyTest(1, "2");
			target.Set(type, key, expected, Scope.Singleton);
			var actual = target.Get(type, key);
			Assert.AreEqual(expected, actual);
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeFromToWithKeyTest()
		{
			var target = InitializeContainerAndRegisterIntAndString();
			var from = typeof(IDependencyTest);
			var expected = typeof(DependencyTest);
			const string key = "Test";
			target.Set(from, expected, key);
			var actual = target.Get(from, key);
			Assert.AreEqual(expected, actual.GetType());
			Assert.AreEqual(((DependencyTest) actual).Id, 1);
			Assert.AreEqual(((DependencyTest) actual).Name, "2");
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeFromToWithKeyAndScopeTest()
		{
			var target = InitializeContainerAndRegisterIntAndString();
			var from = typeof(IDependencyTest);
			var expected = typeof(DependencyTest);
			const string key = "Test";
			target.Set(from, expected, key, Scope.PerResolve);
			var actual = target.Get(from, key);
			Assert.AreEqual(expected, actual.GetType());
			Assert.AreEqual(((DependencyTest) actual).Id, 1);
			Assert.AreEqual(((DependencyTest) actual).Name, "2");
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeFromToWithScopeTest()
		{
			var target = InitializeContainerAndRegisterIntAndString();
			var from = typeof(IDependencyTest);
			var expected = typeof(DependencyTest);
			target.Set(from, expected, Scope.PerResolve);
			var actual = target.Get(from);
			Assert.AreEqual(expected, actual.GetType());
			Assert.AreEqual(((DependencyTest) actual).Id, 1);
			Assert.AreEqual(((DependencyTest) actual).Name, "2");
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeWithScopeTest()
		{
			var target = InitializeContainerAndRegisterIntAndString();
			var expected = typeof(DependencyTest);
			target.Set(expected, Scope.PerResolve);
			var actual = target.Get(expected);
			Assert.AreEqual(expected, actual.GetType());
			Assert.AreEqual(((DependencyTest) actual).Id, 1);
			Assert.AreEqual(((DependencyTest) actual).Name, "2");
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeWithInstanceAndScopeTest()
		{
			var target = InitializeContainerAndRegisterIntAndString();
			var type = typeof(DependencyTest);
			var expected = new DependencyTest(1, "2");
			target.Set(type, expected, Scope.Singleton);
			var actual = target.Get(type);
			Assert.AreEqual(expected, actual);
			Assert.AreEqual(((DependencyTest) actual).Id, 1);
			Assert.AreEqual(((DependencyTest) actual).Name, "2");
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeWithKeyAndScopeTest()
		{
			var target = InitializeContainerAndRegisterIntAndString();
			var expected = typeof (DependencyTest);
			const string key = "Test";
			target.Set(expected, key, Scope.PerResolve);
			var actual = target.Get(expected, key);
			Assert.AreEqual(expected, actual.GetType());
			Assert.AreEqual(((DependencyTest) actual).Id, 1);
			Assert.AreEqual(((DependencyTest) actual).Name, "2");
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeWithKeyAndInstanceTest()
		{
			var target = new Container();
			var type = typeof(DependencyTest);
			const string key = "Test";
			var expected = new DependencyTest(new Dependent());
			target.Set(type, key, expected);
			var actual = target.Get(type, key);
			Assert.AreEqual(expected, actual);
			Assert.IsNotNull(((DependencyTest)actual).Child);
		}

		/// <summary>
		///A test for Register
		///</summary>
		[TestMethod]
		public void RegisterTypeWithInstanceTest()
		{
			var target = new Container();
			var type = typeof(DependencyTest);
			var expected = new DependencyTest(new Dependent());
			target.Set(type, expected);
			var actual = target.Get(type);
			Assert.AreEqual(expected, actual);
			Assert.IsNotNull(((DependencyTest) actual).Child);
		}

		/// <summary>
		///A test for Map
		///</summary>
		public void MapActionTestHelper<T>() where T : new()
		{
			var target = new Container();
			var expected = typeof(T);
			var actual = target.Map<T>().Apply().Get<T>();
			Assert.AreEqual(expected, actual.GetType());
		}

		[TestMethod]
		public void MapActionTest()
		{
            MapActionTestHelper<Dependent>();
		}

		/// <summary>
		///A test for Map
		///</summary>
		[TestMethod]
		public void MapTypeTest()
		{
			var target = InitializeContainerAndRegisterIntAndString();
			var expected = typeof(DependencyTest);
			target.Map(expected).Apply();
			var actual = target.Get(expected);
			Assert.AreEqual(expected, actual.GetType());
		}

		/// <summary>
		///A test for Map
		///</summary>
		public void MapGenericTypeTestHelper<T>()
		{
			var target = new Container();
			var expected = typeof(T);
			var actual = target.Map<T>().Apply().Get<T>();
			Assert.AreEqual(expected, actual.GetType());
		}

		[TestMethod]
		public void MapGenericTypeTest()
		{
			MapGenericTypeTestHelper<Dependent>();
		}

		[TestMethod]
		public void MultipleDependentChainTest()
		{
			var container = InitializeContainerAndRegisterIntAndString();
			container.Set<IDependencyTest, DependencyTest>();
			container.Set<ParentDependent>();
			var parent = container.Get<ParentDependent>();
			Assert.IsNotNull(parent);
			Assert.IsNotNull(parent.Dependency);
			Assert.AreEqual(parent.Dependency.Id, 1);
			Assert.AreEqual(parent.Dependency.Name, "2");
		}

		[TestMethod]
		public void MultipleSingletonResolveTest()
		{
			var container = new Container();
			container.Set<Dependent>();
			container.Set(new DependencyTest(1, "2"));
			container.Set<IDependencyTest, DependencyTest>();
			var expected = container.Get<DependencyTest>();
			for (int i = 0; i < 100; i++)
			{
				var actual = container.Get<DependencyTest>();
				Assert.AreEqual(expected, actual);
				Assert.AreEqual(expected.Id, actual.Id);
				Assert.AreEqual(expected.Name, actual.Name);
			}
		}

		[TestMethod]
		public void MultipleSingletonWithScopeResolveTest()
		{
			var container = InitializeContainerAndRegisterIntAndString();
			container.Set<IDependencyTest, DependencyTest>(Scope.Singleton);
			var expected = container.Get<IDependencyTest>();
			for (int i = 0; i < 100; i++)
			{
				var actual = container.Get<IDependencyTest>();
				Assert.AreEqual(expected, actual);
				Assert.AreEqual(expected.Id, actual.Id);
				Assert.AreEqual(expected.Name, actual.Name);
			}
			container.Set<ParentDependent>();
			var parent = container.Get<ParentDependent>();
			Assert.IsNotNull(parent);
			Assert.IsNotNull(parent.Dependency);
			Assert.AreEqual(expected, parent.Dependency);
			Assert.AreEqual(expected.Id, parent.Dependency.Id);
			Assert.AreEqual(expected.Name, parent.Dependency.Name);
		}

		[TestMethod]
		public void MultipleScopeSingletonWithKeyResolveTest()
		{
			var container = InitializeContainerAndRegisterIntAndString();
			const string key = "key";
			container.Set<DependencyTest>(key, Scope.Singleton);
			var expected = container.Get<DependencyTest>(key);
			for (int i = 0; i < 100; i++)
			{
				var actual = container.Get<DependencyTest>(key);
				Assert.AreEqual(expected, actual);
				Assert.AreEqual(expected.Id, actual.Id);
				Assert.AreEqual(expected.Name, actual.Name);
			}
		}
	}
}